// eee.Sheffield.PZ.Imaging
//
// Copyright ?Ping Zou, 2007
// sg71.cherub@gmail.com

namespace eee.Sheffield.PZ.Imaging
{
    using System;
    using System.IO;
    using System.Drawing;
    using System.Drawing.Imaging;
    using eee.Sheffield.PZ.Math;
    using System.Collections;

    /// <summary>
    /// Liu&Sun 1993
    /// </summary>
    public class LiuSunTracking
    {
        #region Fields
        private PZPoint _cp = new PZPoint();		// center point
        private PZPoint _ep1 = new PZPoint();		// edge point 1
        private PZPoint _ep2 = new PZPoint();		// edge point 2
        private PZDirection _vd = new PZDirection();	// vessel direction
        private int _d = 0;			// forward step
        private SunScanline _lastsl = new SunScanline();	// last scanline, contains the init values
        private SunScanline _currentsl = new SunScanline();	// current scanline
        private bool _isFit = true;
        private string _exitMsg;
        private Bitmap _scanlineImage;
        #endregion

        #region Properties
        public PZPoint CentrePoint { get { return _cp; } }
        public PZPoint EdgePoint1 { get { return _ep1; } }
        public PZPoint EdgePoint2 { get { return _ep2; } }
        public PZDirection VesselDirection { get { return _vd; } }
        public int D { get { return _d; } set { _d = value; } }
        public SunScanline LastScanline { get { return _lastsl; } }
        public SunScanline CurrentScanline { get { return _currentsl; } set { _currentsl.MemCopyFrom(value); } }
        public bool IsFit { get { return _isFit; } }
        public string ExitMsg { get { return _exitMsg; } }
        public Bitmap ScanlineImage { get { return _scanlineImage; } }
        #endregion

        #region Constructors
        /// <summary>
        /// empty constructor
        /// </summary>
        public LiuSunTracking() { }

        /// <summary>
        /// constructor
        /// </summary>
        public LiuSunTracking(PZPoint inputp, PZDirection inputvd,
            SunScanline inputsl, int inputd)
        {
            _cp.MemCopyFrom(inputp);
            _vd.MemCopyFrom(inputvd);
            _d = inputd;
            _lastsl.MemCopyFrom(inputsl);
            _ep1 = new PZPoint();
            _ep2 = new PZPoint();

            // -- init current scanline
            _currentsl.MemCopyFrom(_lastsl);
        } // LiuSunTracking()
        #endregion

        /// <summary>
        /// scanline tracking, Sun 1989, follows one vessel segment
        /// </summary>
        /// <param name="srcImg"></param>
        /// <returns></returns>
        public Bitmap Apply(Bitmap srcImg)
        {
            _scanlineImage = new Bitmap(srcImg);
            // -- prepare varibales;
            int width = srcImg.Width;
            int height = srcImg.Height;
            double initr = _currentsl.InitRadius;
            int widthOfScanline = _currentsl.WidthOfScanline;
            double gamaThreshold = _currentsl.GamaTherehold;

            int halfWidth = widthOfScanline / 2;                           

            // -- prepare for debug file
            string slRecordFilenName = "scanlineRecord.txt";
            FileStream slRecordFileStream = new FileStream(slRecordFilenName, FileMode.Append, FileAccess.Write);
            StreamWriter slRecordStreamWriter = new StreamWriter(slRecordFileStream);
            string cpString, vdString;            

            // -- do sun predict-adjust tracking
            // 1. p~(k + d) = p(k) + d * u^(k)
            //      p(k) = _cp
            //      u^(k) = _vd
            PZPoint cpWkd = new PZPoint();
            cpWkd.ExtendFrom(_cp, _vd, _d);

            // 2. extract scanline profile g~(k + d), and do matched filter
            PZDirection sd1 = new PZDirection(_vd);
            sd1.GetNormalDirection();		// scanline direction
            ExtractScanline exsl1 = new ExtractScanline(cpWkd, sd1, 1, widthOfScanline);
            exsl1.Apply(srcImg);
            SunScanline gWkd = new SunScanline(initr, gamaThreshold);
            gWkd.ScanlineProfile.MemCopyFrom(exsl1.Fg);
            Array.Copy(exsl1.Fp, gWkd.ScanlinePoint, exsl1.Fp.Length);
            //for (int i = 0; i < widthOfScanline; i++)
            //{
            //    gWkd.ScanlineProfile[i] = exsl1.Fg(i);
            //    gWkd.ScanlinePoint[i].MemCopyFrom(exsl1.Fp(i));
            //}
            gWkd.Analysis();

            // 3. estiamte p'(k + d) from matched filter output
            PZPoint cpPkd = new PZPoint();
            cpPkd.ExtendFrom(cpWkd, sd1, gWkd.Centre - halfWidth);

            // 4. update u^(k + d) by p'(k + d) and p(k)
            PZDirection vdkd = new PZDirection();
            vdkd.GetDirectionFrom2Points(_cp, cpPkd);

            // 5. update scanline profile g(k + d), and do matched filter
            PZDirection sd2 = new PZDirection(_vd);
            sd2.GetNormalDirection();		// scanline direction
            ExtractScanline exsl2 = new ExtractScanline(cpPkd, sd2, 1, widthOfScanline);
            exsl2.Apply(srcImg);
            SunScanline gkd = new SunScanline(initr, gamaThreshold);
            gkd.ScanlineProfile.MemCopyFrom(exsl2.Fg);
            Array.Copy(exsl2.Fp, gkd.ScanlinePoint, exsl2.Fp.Length);
            //for (int i = 0; i < widthOfScanline; i++)
            //{
            //    gkd.ScanlineProfile[i] = exsl2.Fg(i);
            //    gkd.ScanlinePoint[i].MemCopyFrom(exsl2.Fp(i));
            //}
            gkd.Analysis();
            gkd.WriteFile("scanline.txt");

            // 6. determin S(k + d) and B(k + d) by g(k + d) and p'(k + d)
            double skd = gkd.SignalLevel;
            double bkd = gkd.BackgroundLevel;
            // 7. get edge positions, radius estimate, and p(k + d)
            PZMath_vector edgePositions = gkd.EdgePositions;
            PZPoint ep1 = new PZPoint();
            PZPoint ep2 = new PZPoint();
            ep1.ExtendFrom(cpPkd, sd2, edgePositions[0] - halfWidth);
            ep2.ExtendFrom(cpPkd, sd2, edgePositions[1] - halfWidth);
            double r = gkd.Radius;
            double centrePosition = (edgePositions[0] + edgePositions[1]) / 2.0;
            PZPoint cpkd = new PZPoint();
            cpkd.ExtendFrom(cpPkd, sd2, centrePosition - halfWidth);


            // plot gkd, cp, & eps on srcImage
            for (int i = 0; i < widthOfScanline; i++)
            {
                int x = (int)gkd.ScanlinePoint[i].x;
                int y = (int)gkd.ScanlinePoint[i].y;
                if (x >= 0 && x < width && y >= 0 && y < height)
                    _scanlineImage.SetPixel(x, y, Color.Yellow);
            }
            int ep1x = (int)ep1.x;
            int ep1y = (int)ep1.y;
            if (ep1x >= 0 && ep1x < width && ep1y >= 0 && ep1y < height)
                _scanlineImage.SetPixel(ep1x, ep1y, Color.Green);
            int ep2x = (int)ep2.x;
            int ep2y = (int)ep2.y;
            if (ep2x >= 0 && ep2x < width && ep2y >= 0 && ep2y < height)
                _scanlineImage.SetPixel(ep2x, ep2y, Color.Blue);
            int cpx = (int)cpkd.x;
            int cpy = (int)cpkd.y;
            if (cpx >= 0 && cpx < width && cpy >= 0 && cpy < height)
                _scanlineImage.SetPixel(cpx, cpy, Color.Red);

            _scanlineImage.Save("scanline plot.bmp", ImageFormat.Bmp);

            // 7. check scanline fit
            if (!gkd.IsFit())
            {
                _exitMsg = gkd.ExitMsg;
                _isFit = false;
            }                                 

            // record scanline analysis result
            cpString = _cp.ToString();
            vdString = _vd.ToString();
            slRecordStreamWriter.Write(String.Format("{0, -25}{1, -25}", cpString, vdString));
            cpString = cpWkd.ToString();
            slRecordStreamWriter.Write(String.Format("{0, -25}", cpString));
            cpString = cpPkd.ToString();
            vdString = vdkd.ToString();
            slRecordStreamWriter.Write(String.Format("{0, -25}{1, -25}", cpString, vdString));
            slRecordStreamWriter.Write(String.Format("{0, -7:f}{1, -7:f}", skd, bkd));
            slRecordStreamWriter.Write(String.Format("{0, -7:f}{1, -7:f}", edgePositions[0], edgePositions[1]));
            slRecordStreamWriter.Write(String.Format("{0, -7:f}", r));
            cpString = cpkd.ToString();
            slRecordStreamWriter.Write(String.Format("{0, -25}", cpString));
            slRecordStreamWriter.WriteLine(String.Format("{0, -7:f}{1, -7}", gkd.Gama, _isFit.ToString()));

            // save current scanline analysis result
            _currentsl.MemCopyFrom(gkd);            

            _cp.MemCopyFrom(cpkd);
            _vd.MemCopyFrom(vdkd);
            _ep1.MemCopyFrom(ep1);
            _ep2.MemCopyFrom(ep2);
                     
            slRecordStreamWriter.Close();
            slRecordFileStream.Close();

            // -- return an useless dstImg
            Bitmap dstImg = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
            return dstImg;
        } // Apply()


    } // ConsequtiveScanlineTracking
}
